﻿using System;
using System.Collections.Generic;
using UnityEditor;
using UnityEngine;

namespace PerfAssist.Common.Editor.TableView
{
    public partial class TableView
    {
        private Vector2 _scrollPos = Vector2.zero;

        private int _sortSlot;

        private readonly List<TableViewColDesc> m_descArray = new List<TableViewColDesc>();
        private readonly EditorWindow m_hostWindow;
        private readonly Type m_itemType;
        private readonly List<object> m_lines = new List<object>();

        private object m_selected;
        private int m_selectedCol = -1;

        private Dictionary<object, Color> m_specialTextColors;

        public bool Descending { get; set; }

        private void DrawTitle(float width)
        {
            for (var i = 0; i < m_descArray.Count; i++)
            {
                var desc = m_descArray[i];

                var r = LabelRect(width, i, 0);
                var selected = _sortSlot == i;
                GUI.Label(r, desc.TitleText + (selected ? Appearance.GetSortMark(Descending) : ""),
                    Appearance.GetTitleStyle(selected));
                if (Event.current.type == EventType.MouseDown && r.Contains(Event.current.mousePosition))
                {
                    if (_sortSlot == i)
                        Descending = !Descending;
                    else
                        _sortSlot = i;

                    SortData();
                    m_hostWindow.Repaint();
                }
            }
        }

        private void DrawLine(int pos, object obj, float width)
        {
            var r = new Rect(0, pos * Appearance.LineHeight, width, Appearance.LineHeight);
            var selectionHappens = Event.current.type == EventType.MouseDown && r.Contains(Event.current.mousePosition);

            var style = new GUIStyle(pos % 2 != 0 ? Appearance.Style_Line : Appearance.Style_LineAlt);
            if (selectionHappens) m_selected = obj;

            // note that the 'selected-style' assignment below should be isolated from the if-conditional statement above
            // since the above if is a one-time event, on the contrary, the 'selected-style' assignment below should be done every time in the drawing process
            if (m_selected == obj)
            {
                style = Appearance.Style_Selected;
            }
            else
            {
                Color specialColor;
                if (m_specialTextColors != null &&
                    m_specialTextColors.TryGetValue(obj, out specialColor))
                    style.normal.textColor = specialColor;
            }

            for (var i = 0; i < m_descArray.Count; i++)
                DrawLineCol(pos, i, width, obj, style, selectionHappens);
        }

        private void DrawLineCol(int pos, int col, float width, object obj, GUIStyle style, bool selectionHappens = false)
        {
            var rect = LabelRect(width, col, pos);

            var desc = m_descArray[col];
            var text = desc.FormatObject(obj);

            if (selectionHappens && rect.Contains(Event.current.mousePosition))
            {
                m_selectedCol = col;
                if (OnSelected != null)
                    OnSelected(obj, col);

                EditorGUIUtility.systemCopyBuffer = text;
                m_hostWindow.Repaint();
            }

            // internal sequential id
            if (ShowInternalSeqID && col == 0)
                text = pos + ". " + text;

            // note that the 'selected-style' assignment below should be isolated from the if-conditional statement above
            // since the above if is a one-time event, on the contrary, the 'selected-style' assignment below should be done every time in the drawing process
            if (m_selectedCol == col && m_selected == obj) style = Appearance.Style_SelectedCell;

            style.alignment = desc.Alignment;
            GUI.Label(rect, new GUIContent(text, text), style);
        }

        private void SortData()
        {
            m_lines.Sort((s1, s2) =>
            {
                if (_sortSlot >= m_descArray.Count)
                    return 0;

                return m_descArray[_sortSlot].Compare(s1, s2) * (Descending ? -1 : 1);
            });
        }

        private Rect LabelRect(float width, int slot, int pos)
        {
            var accumPercent = 0.0f;
            var count = Mathf.Min(slot, m_descArray.Count);
            for (var i = 0; i < count; i++) accumPercent += m_descArray[i].WidthInPercent;
            return new Rect(width * accumPercent, pos * Appearance.LineHeight, width * m_descArray[slot].WidthInPercent,
                Appearance.LineHeight);
        }
    }
}